﻿// -----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// -----------------------------------------------------------------------
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Internal;
using Microsoft.Internal.Collections;

namespace System.ComponentModel.Composition.ReflectionModel
{
    internal abstract class ImportingItem
    {
        private readonly ContractBasedImportDefinition _definition;
        private readonly ReflectionItem _item;
        private readonly ImportType _importType;

        protected ImportingItem(ContractBasedImportDefinition definition, ReflectionItem item)
        {
            Assumes.NotNull(definition, item);

            _definition = definition;
            _item = item;
            _importType = new ImportType(_item.ReturnType);
        }

        public ContractBasedImportDefinition Definition
        {
            get { return _definition; }
        }

        public ImportType ImportType
        {
            get { return _importType; }
        }

        public CompositionResult<object> TryCastExportsToImportType(IEnumerable<Export> exports)
        {
            Assumes.NotNull(exports);

            if (this.Definition.Cardinality == ImportCardinality.ZeroOrMore)
            {
                return this.TryCastExportsToCollectionImportType(exports);
            }
            else
            {
                return this.TryCastExportsToSingleImportType(exports);
            }
        }

        private CompositionResult<object> TryCastExportsToCollectionImportType(IEnumerable<Export> exports)
        {
            Assumes.NotNull(exports);

            CompositionResult result = CompositionResult.SucceededResult;

            if (this.ImportType.IsLazy)
            {
                return result.ToResult<object>(ExportServices.CreateStronglyTypedExportsFromExports(this.ImportType.LazyType, exports));
            }

            IList list = CollectionServices.CreateGenericList(this.ImportType.ElementType);

            foreach (Export export in exports)
            {
                CompositionResult<object> valueResult = export.TryGetExportedObject();
                result = result.MergeResult(valueResult.ToResultWithNoValue());

                if (!result.Succeeded)
                {
                    return result.ToResult<object>(null);
                }

                object castedValue;
                if (!ContractServices.TryCast(this.ImportType.ElementType, valueResult.Value, out castedValue))
                {
                    return result.MergeIssue(CompositionIssue.CreateIssueAsError(
                        CompositionIssueId.TypeMismatchBetweenImportAndExport,
                        Strings.TypeMismatchBetweenImportAndExport,
                        this.Definition.ContractName,
                        valueResult.Value.GetType().FullName,
                        this.ImportType.ElementType.FullName)).ToResult<object>(null);
                }

                list.Add(castedValue);
            }

            return result.ToResult<object>(list);
        }

        private CompositionResult<object> TryCastExportsToSingleImportType(IEnumerable<Export> exports)
        {
            Assumes.NotNull(exports);
            CompositionResult result = CompositionResult.SucceededResult;

            Export export = exports.FirstOrDefault();
            if (export == null)
            {
                return result.ToResult<object>(null);
            }

            if (this.ImportType.IsLazy)
            {
                return result.ToResult<object>(ExportServices.CreateExport(this.ImportType.LazyType.ElementExportType, export));
            }

            CompositionResult<object> valueResult = export.TryGetExportedObject();
            if (!valueResult.Succeeded)
            {
                return result.MergeResult(valueResult.ToResultWithNoValue()).ToResult<object>(null);
            }

            object castedValue;
            if (!ContractServices.TryCast(this._importType.Type, valueResult.Value, out castedValue))
            {
                return result.MergeIssue(CompositionIssue.CreateIssueAsError(
                    CompositionIssueId.TypeMismatchBetweenImportAndExport,
                    Strings.TypeMismatchBetweenImportAndExport,
                    this.Definition.ContractName,
                    valueResult.Value.GetType().FullName,
                    this._importType.Type.Name)).ToResult<object>(null);
            }

            return result.ToResult<object>(castedValue);
        }
    }
}
